/*
  The stuffer.c file implements the stuffer file system.
*/

#include <types.h>
#include <klib.h>
#include <stuffer.h>
#include <globals.h>     /* RELOC_PATCH      */
#include <multiboot.h>   /* GRUB_boot_header */
#include <panic.h>
#include <linkedlist.h>
#include <scheduler.h>   /* process_image_list */

// 64 KB
#define STUFFER_BUFFER_SIZE         65536

// Temporary storage to keep the stuffer archive
byte_t stuffer_buffer[STUFFER_BUFFER_SIZE];

int modules_number;
int stuffer_size;
int magic;
enum stuffer_errors_t stuffer_error_code;

// private functions prototypes
void extract_file(struct stuffer_entry_t *sfs_entry);


// Warning: to be called before paging is enabled...
void stuffer_initialize(void)
{
  struct Multiboot_Info_T *mbi;
  struct ModulesList_T *mod;
  unsigned int i;
  int *smagic;

  // Clear buffer (for safety)
  for (i=0; i<STUFFER_BUFFER_SIZE; i++)
    stuffer_buffer[i]='\0';
  
  // suppose no archive has been loaded
  stuffer_error_code=SE_NOARCHIVE;

  mbi=(struct Multiboot_Info_T *)GRUB_boot_header;

  modules_number=0;
  stuffer_size=0;
  
  // Are mods_* valid? 	
  if (CHECK_FLAG(mbi->flags,3))
    {
      // How many modules are there
      modules_number=mbi->mods_count;

      // SE_NOARCHIVE has already been set
      if (modules_number == 0)
	return;

      // if we have exactly one module
      // KERNEL EXPECTS ONLY ONE
      if (modules_number == 1)
	{
	  mod = (struct ModulesList_T *) (mbi->mods_addr + RELOC_PATCH);
	  
	  // retrieve stuffer magic number
	  // to check whether it's a stuffer archive
	  smagic=(int *)(mod->mod_start+RELOC_PATCH);
	  magic=*smagic;
	  // is it a real stuffer archive
	  if (*smagic == STUFFER_MAGIC)
	    {
	      // calculate archive size
	      stuffer_size = (int)(mod->mod_end - mod->mod_start);

	      // copy stuffer archive into internal buffer declared above
	      // if it's not too big
	      if (stuffer_size>0 && stuffer_size<STUFFER_BUFFER_SIZE)
		{
		  memcpy((dword_t)(stuffer_buffer),(dword_t)(mod->mod_start+RELOC_PATCH),(size_t)stuffer_size);
		  stuffer_error_code=SE_OK;
		}
	      else
		stuffer_error_code=SE_TOOBIG;
	    }
	  else
	    stuffer_error_code=SE_INVALID;
	}
      else
	stuffer_error_code=SE_TOOMANY;
    }  
}


int stuffer_get_status(void)
{
  return (int)stuffer_error_code;
}


void stuffer_show_information(void)
{
  char *msg;

  /*
  printf("Contents:\n");
  printf("==================================================\n");
  memory_dump(stuffer_buffer,stuffer_size);
  printf("==================================================\n");

  printf("Stuffer size: %d\n",stuffer_size);
  printf("Stuffer magic: %x\n",magic);
  */
  
  switch(stuffer_error_code)
    {
    case SE_OK:
      msg="OK";
      break;
      
    case SE_TOOBIG:
      msg="Archive too big";
      break;

    case SE_INVALID:
      msg="Invalid archive";
      break;

    case SE_NOARCHIVE:
      msg="No archive found";
      break;

    case SE_TOOMANY:
      msg="Too many modules";
      break;

    default:
      msg="Error";
    }

  printf("Stuffer: ");
  if (stuffer_size>0)
    printf("loaded (%s)\n",msg);
  else
    printf("not loaded (%s)\n",msg);
}

void stuffer_make_process_list(void)
{  
  struct stuffer_header_t *sfs_header;
  struct stuffer_entry_t *sfs_entry;
  int i;
  int file_index;

  process_image_list=llist_make_list();

  file_index=0;

  // point to header
  sfs_header=(struct stuffer_header_t *)stuffer_buffer;
  //sfs_entry=(struct stuffer_entry_t *)malloc(sizeof(struct stuffer_entry_t));

  file_index=sfs_header->num_files;

  for (i=0;i<file_index;i++)
    {
      // point to i-th entry
      sfs_entry=(struct stuffer_entry_t *)((int)stuffer_buffer + sizeof(struct stuffer_header_t) + i * sizeof(struct stuffer_entry_t));

      //printf("@ %d\n",sfs_entry->offset);
      //printf("%d:\t----\t%d\t%s\n",sfs_entry->index,sfs_entry->size,sfs_entry->name);
      //printf("Extracting: %s\n",sfs_entry->name);
      extract_file(sfs_entry);
    }
}

void extract_file(struct stuffer_entry_t *sfs_entry)
{
  int i;
  image_t *image_entry;
  dword_t offset;

  // allocate space for entry
  image_entry=(image_t *)malloc(sizeof(image_t));

  // allocate space for image
  image_entry->data=(byte_t *)malloc(sizeof(byte_t) * sfs_entry->size);
  image_entry->size=sfs_entry->size;

  // copy name
  strcpy(image_entry->name,sfs_entry->name);

  // copy file data
  offset=(dword_t)stuffer_buffer;
  offset+=sfs_entry->offset;
  memcpy(image_entry->data,offset,(size_t)(sfs_entry->size));

  // add image to list
  llist_add_last(process_image_list,image_entry);
}
